/*****************************************************************************
*    Copyright (C)2007 Ali Corporation. All Rights Reserved.
*
*    File:    tun_av2000.c
*
*    Description:    Source file for AV2000 for AIROHA
*    History:
*      	Date            		Athor    				Version          Reason
*	============	=============	=========	=================
*	1.  Aug.16.2007		Penghui				Ver 0.1		Create file.
*****************************************************************************/

#include <sys_config.h>
#include <retcode.h>
#include <types.h>
#include <api/libc/printf.h>
#include <api/libc/string.h>
#include <bus/i2c/i2c.h>
#include <osal/osal.h>
#include "nim_av2000.h"

#define NIM_TUNER_PRINTF(...)

static UINT32 av2000_tuner_cnt = 0;
static struct QPSK_TUNER_CONFIG_EXT * av2000_dev_id[MAX_TUNER_SUPPORT_NUM] = {NULL};
static unsigned char tuner_initial[MAX_TUNER_SUPPORT_NUM] = {0,0}; 
static unsigned short tuner_crystal_fixed = 27; // unit is MHz

// I2C write function (register start address, register array pointer, register length)
static int Tuner_I2C_write(UINT32 tuner_id, unsigned char reg_start, unsigned char* buff, unsigned char length)
{
	UINT8 data[16];
	UINT32 rd = 0;
	int i2c_result;
	struct QPSK_TUNER_CONFIG_EXT * av2000_ptr = NULL;

	av2000_ptr = av2000_dev_id[tuner_id];	
	data[0] = reg_start;

	while((rd+15)<length)
	{
		MEMCPY(&data[1], &buff[rd], 15);
		i2c_result = i2c_write(av2000_ptr->i2c_type_id, av2000_ptr->cTuner_Base_Addr, data, 16);
		rd+=15;
		data[0] += 15;
		if(SUCCESS != i2c_result)
			return i2c_result;
	}
	MEMCPY(&data[1], &buff[rd], length-rd);
	i2c_result = i2c_write(av2000_ptr->i2c_type_id, av2000_ptr->cTuner_Base_Addr, data, length-rd+1);

	return i2c_result;
}

static int Tuner_I2C_read(UINT32 tuner_id, unsigned char reg_start, unsigned char* buff, unsigned char length)
{
	UINT8 data[16];
	UINT32 rd = 0;
	int i2c_result;
	struct QPSK_TUNER_CONFIG_EXT * av2000_ptr = NULL;

	av2000_ptr = av2000_dev_id[tuner_id];	
	data[0] = reg_start;

	while((rd+15)<length)
	{
		i2c_result = i2c_write_read(av2000_ptr->i2c_type_id, av2000_ptr->cTuner_Base_Addr, 1, data, 15);
		MEMCPY(&buff[rd], &data[0], 15);
		rd+=15;
		data[0] += 15;
		if(SUCCESS != i2c_result)
			return i2c_result;
	}
	i2c_result = i2c_write_read(av2000_ptr->i2c_type_id, av2000_ptr->cTuner_Base_Addr, data, 1, length-rd);
	MEMCPY(&buff[rd], &data[0], 15);
	return i2c_result;
}

static void Time_DELAY_MS (unsigned int ms)
{
	osal_task_sleep(ms);
}


/*****************************************************************************
* INT32 tun_av2000_init(UINT32 para, struct QPSK_TUNER_CONFIG_EXT * ptrTuner_Config)
*
* Tuner philips Initialization
*
* Arguments:
*  Parameter1: UINT32 para		: Input Parameter
*  Parameter2: struct QPSK_TUNER_CONFIG_EXT * ptrTuner_Config		: pointer for Tuner configuration structure
*
* Return Value: INT32			: Result
*****************************************************************************/
INT32 nim_av2000_init(UINT32 * tuner_id,  struct QPSK_TUNER_CONFIG_EXT * ptrTuner_Config)
{
	INT32 result;
	struct QPSK_TUNER_CONFIG_EXT* av2000_ptr=NULL;
	
	/* check Tuner Configuration structure is available or not */
	if (ptrTuner_Config == NULL||av2000_tuner_cnt>=MAX_TUNER_SUPPORT_NUM)
		return ERR_FAILUE;
	av2000_ptr = (struct QPSK_TUNER_CONFIG_EXT *)MALLOC(sizeof(struct QPSK_TUNER_CONFIG_EXT));
	if(!av2000_ptr)
	      return ERR_FAILUE;

	MEMCPY(av2000_ptr, ptrTuner_Config, sizeof(struct QPSK_TUNER_CONFIG_EXT));
	av2000_dev_id[av2000_tuner_cnt] = av2000_ptr;
	*tuner_id = av2000_tuner_cnt;
	av2000_tuner_cnt++;
	
	return SUCCESS;
	
}


/*****************************************************************************
* INT32 tun_av2000_control(UINT32 freq, UINT32 sym)
*
* Tuner write operation
*
* Arguments:
*  Parameter1: UINT32 freq		: Synthesiser programmable divider
*  Parameter2: UINT32 sym			: Baseband Symbol Rate
*  Parameter3: UINT8 cp			: Charge pump current
*
* Return Value: INT32			: Result
*****************************************************************************/
INT32 nim_av2000_control(UINT32 tuner_id, UINT32 freq, UINT32 sym)
{
//joey 20080509. enlarge the array size.
	UINT8 data[50], i, Start_Mem_Addr;
	UINT32 divider;
	UINT32 BW;
	UINT32 tmp_data;
	UINT8 auto_scan = 0;

	struct QPSK_TUNER_CONFIG_EXT * av2000_ptr=NULL;
	
	//static UINT8 init_act = 0;

	if(tuner_id >= av2000_tuner_cnt || tuner_id>=MAX_TUNER_SUPPORT_NUM)
		return ERR_FAILUE;
	av2000_ptr= av2000_dev_id[tuner_id];

	//when sym is 0, may means auto-scan.
	if ((sym == 0) ||(sym == 45000)) //auto-scan mode
	{
		auto_scan = 1;
	}
	if (tuner_initial[tuner_id] == 0) // do the init if not do yet.
	{
        //joey 20080509. for all register initialize..

        if (av2000_ptr->wTuner_Crystal == 27000)
        	{
               	 data[0]=(char) (0x38);
                 data[9]=(char) (0x82);	
            }
        else //if (m_QpskTunerConfig.wTuner_Crystal == 13500)
	        {
			     data[0]=(char) (0x70); //XO=13.5MHz
			     data[9]=(char) (0x80); //XO select 13.5MHz(D2=0); (27MHz D2=1)	
	        }
		data[1]=(char) (0x00);
		data[2]=(char) (0x00);
		data[3]=(char) (0x50);
		data[4]=(char) (0x1f);
		data[5]=(char) (0xa3);
		data[6]=(char) (0xfd);
		data[7]=(char) (0x58);
		data[8]=(char) (0x0e);		
		data[10]=(char) (0x88);
		data[11]=(char) (0xb4);
		
        data[12]=(char) (0xD6);	//RFLP=ON at Power on initial
        
		data[13]=(char) (0x40);
		data[14]=(char) (0x5b);
		data[15]=(char) (0x6a);
		data[16]=(char) (0x66);
		data[17]=(char) (0x40); 
		data[18]=(char) (0x80);	
		data[19]=(char) (0x2b);
		data[20]=(char) (0x6a);
		data[21]=(char) (0x50);	
		data[22]=(char) (0x91);
		data[23]=(char) (0x27); 
		data[24]=(char) (0x8f);
		
		data[25]=(char) (0xcc);
		data[26]=(char) (0x21);
		data[27]=(char) (0x10);
		data[28]=(char) (0x80);
		data[29]=(char) (0x02);
		data[30]=(char) (0xf5);
		data[31]=(char) (0x7f);
		data[32]=(char) (0x4a);
		data[33]=(char) (0x9b);
		data[34]=(char) (0xe0);
		data[35]=(char) (0xe0);
		
		data[36]=(char) (0x36);
//joey 20080505. for av2000C fine-tune gain function enable. bit1.	
		data[37]=(char) (0x00); //monsen 20080710. Disble FT-function at Power on initial
      //data[37]=(char) (0x02);
//joey 20080520. for av2000C fine-tune. register add part.
		data[38]=(char) (0xab);
		data[39]=(char) (0x97);
		data[40]=(char) (0xc5);
		data[41]=(char) (0xa8);

//joey 20080509. the init write action.
		for(i=0;i<42;i++)
		{
			NIM_TUNER_PRINTF("///---Reg %d is 0x%x\n", i ,data[i]);
		}
		
		//monsen 20080709. power on initial
        NIM_TUNER_PRINTF("///---Power ON initial Sequence:\n");
        
        // Sequence 1    
		NIM_TUNER_PRINTF("///---Sequence 1:\n");
		// Send Reg0 ->Reg11 
		Start_Mem_Addr = 0;
		Tuner_I2C_write(tuner_id,Start_Mem_Addr,data,12);
		Time_DELAY_MS(1);
             
		// Sequence 2A
		NIM_TUNER_PRINTF("///---Sequence 2A:\n");
		//because I2C master limit, max data can 15 byte.
		// Send Reg13 ->Reg24
		Start_Mem_Addr = 13;
		Tuner_I2C_write(tuner_id,Start_Mem_Addr,data+13,12);
		
        // Sequence 2B
		NIM_TUNER_PRINTF("///---Sequence 2B:\n");
    	// Send Reg25 ->Reg35
		Start_Mem_Addr = 25;
		Tuner_I2C_write(tuner_id, Start_Mem_Addr,data+25,11);

        // Sequence 2C
		NIM_TUNER_PRINTF("///---Sequence 2C:\n");
		// Send Reg36 ->Reg41
		Start_Mem_Addr = 36;
		Tuner_I2C_write(tuner_id, Start_Mem_Addr,data+36,6);		

		Time_DELAY_MS(1);
		
        // Sequence 3
		NIM_TUNER_PRINTF("///---Sequence 3:\n");
		// send reg 12		
		Start_Mem_Addr = 12;
		Tuner_I2C_write(tuner_id, Start_Mem_Addr,data+12,1);
	
		// Wait 100 ms	20081125	
		Time_DELAY_MS(100);
		
		// Reinitial again
		{
		   // Sequence 1    
		   NIM_TUNER_PRINTF("///---Sequence 1:\n");
		   // Send Reg0 ->Reg11 
		   Start_Mem_Addr = 0;
		   Tuner_I2C_write(tuner_id, Start_Mem_Addr,data,12);
		   Time_DELAY_MS(1);
             
	           // Sequence 2A
	           NIM_TUNER_PRINTF("///---Sequence 2A:\n");
	           //because I2C master limit, max data can 15 byte.
	           // Send Reg13 ->Reg24
	           Start_Mem_Addr = 13;
	           Tuner_I2C_write(tuner_id, Start_Mem_Addr,data+13,12);
			
	           // Sequence 2B
	           NIM_TUNER_PRINTF("///---Sequence 2B:\n");
	           // Send Reg25 ->Reg35
	           Start_Mem_Addr = 25;
	           Tuner_I2C_write(tuner_id, Start_Mem_Addr,data+25,11);

	           // Sequence 2C
	           NIM_TUNER_PRINTF("///---Sequence 2C:\n");
	           // Send Reg36 ->Reg41
	           Start_Mem_Addr = 36;
	           Tuner_I2C_write(tuner_id, Start_Mem_Addr,data+36,6);		

	           Time_DELAY_MS(1);
		
		   // Sequence 3
		   NIM_TUNER_PRINTF("///---Sequence 3:\n");
		   // send reg 12		
		   Start_Mem_Addr = 12;
		   Tuner_I2C_write(tuner_id, Start_Mem_Addr,data+12,1);
		
		}
		
		// Wait 4 ms		
		Time_DELAY_MS(4);
		
		// power on initial end
		tuner_initial[tuner_id] =1;
 		// Time delay 4ms
		Time_DELAY_MS(4);
	}
	else //monsen 20080709. Lock Channel sequence 
	{
        //Make sure Config Demod first, then Config AV2000 
         
	    //Channel Freg register calculation.  
    		//divider = (freq+tuner_crystal_fixed/2)/tuner_crystal_fixed;
    		divider = 2*freq + tuner_crystal_fixed;
		divider = divider / (2*tuner_crystal_fixed);
    		if(divider > 0xff)
    			divider = 0xff;
    		data[0]=(char) (divider&0xff);
	//old
    		//divider = ((freq<<17)+tuner_crystal_fixed/2)/tuner_crystal_fixed;
		//divider = (freq<<17)/tuner_crystal_fixed;
	// new, it should be more accurate, by seen, 20100921
		tmp_data = tuner_crystal_fixed*divider;
		if(freq>=tmp_data)  // get divider abs
			divider = freq - tmp_data;
		else
			divider = tmp_data-freq;
		divider = (divider<<17) / tuner_crystal_fixed;
		if(freq<tmp_data)
			divider = (1<<17) - divider;
			
    		//if(divider>0x1ffff)
    		divider = divider & 0x1ffff;
    		data[1]=(char) ((divider>>9)&0xff);
    		data[2]=(char) ((divider>>1)&0xff);
        	//joey 20080521. for Av2000C.	
	    	data[3]=(char) (((divider<<7)&0x80) | 0x50);	// default is 0x50

		Time_DELAY_MS(4);
    	//Baseband 3db Filter caculation.
    	//"sym" unit is hz;
    	if(auto_scan)
    	{
    		data[5] = 0xb5;//0xA3;
    	}
    	else
    	{
    		// rolloff is 35%
    		BW = sym*135/200;

    			// 0.1dB to 3dB need about 30% transition band
    			//BW = BW*1.3;

    			// add 3M when Rs<6.5M
    		if(sym<6500)  
    			BW = BW + 6000;

    			// add 2M for LNB frequency shifting
    		BW = BW + 2000;

    			// add 8% margin since fc is not very accurate	
    		BW = BW*108/100;

    			// Maxim LPF can be tuned form 4M to 40M, step is 0.29M
    		if(BW< 4000)
    			BW = 4000;
    		if(BW>40000)
    			BW = 40000; 
    		tmp_data = (BW*127 + 21100/2) / (21100); // 	BW(MHz) * 1.27 / 211KHz
    		data[5] = (UINT8)tmp_data;
    	}
    	
    	// Sequence 4	
    	// write the freq config:
    	Tuner_I2C_write(tuner_id, 0,data,4);
    	// monsen 20080709. Time_DELAY_MS(1);	
  	    Time_DELAY_MS(4);
    	
   		// Sequence 5
    	// write the bandwidth config:    	
    	Tuner_I2C_write(tuner_id, 5, data+5, 1);
	    // monsen 20080709. Time_DELAY_MS(1);

        //joey 20080505. for av2000C fine-tune gain function block. bit2.
		Time_DELAY_MS(4);
    	if (!auto_scan) //not auto-scan case. enable block function. 
    	{

    		data[37] = 0x06;
    		Tuner_I2C_write(tuner_id, 37, data+37, 1);

	    	//Disable RFLP at Lock Channel sequence after data[37]
	    	//RFLP=OFF at Lock Channel sequence
            // RFLP can be Turned OFF, only at Receving mode. 	
            //data[12] = 0x96;  //RFLP=OFF
			data[12] = 0xd6;   //RFLP=ON        
	    	Tuner_I2C_write(tuner_id, 12, data+12, 1); 
		Time_DELAY_MS(4);    	    	
            
    	}else
	{
		data[37] = 0x05;
    		Tuner_I2C_write(tuner_id, 37, data+37, 1);
		Time_DELAY_MS(4);
	}
    	
        //monsen 20080709. For quick register sending timing. Move Debug information Display 
        NIM_TUNER_PRINTF("///---Lock Channel Sequence:\n"); 
       	NIM_TUNER_PRINTF("///---Sequence 4:\n");  
	    NIM_TUNER_PRINTF("///---freq is  %d\n",freq);
	    NIM_TUNER_PRINTF("///---RS   is  %d\n",sym/1000); 
   		NIM_TUNER_PRINTF("///---Sequence 5:\n");  	   
        NIM_TUNER_PRINTF("///---BW   is  %d\n",BW);
        
    	if (!auto_scan) //not auto-scan case. enable block function. 
        {
            NIM_TUNER_PRINTF("///---FT_Block:\n");	
		    NIM_TUNER_PRINTF("///---Reg37 is %x\n",data[37]);
		    NIM_TUNER_PRINTF("///---Reg12 is %x\n",data[12]);
	    }
	}
    
	return SUCCESS;
}

/*****************************************************************************
* INT32 tun_av2000_status(UINT8 *lock)
*
* Tuner read operation
*
* Arguments:
*  Parameter1: UINT8 *lock		: Phase lock status
*
* Return Value: INT32			: Result
*****************************************************************************/
INT32 nim_av2000_status(UINT32 tuner_id, UINT8 *lock)
{
	if (tuner_id>=av2000_tuner_cnt||tuner_id>=MAX_TUNER_SUPPORT_NUM)
	{
		*lock = 0;
		return ERR_FAILUE;
	}
	/* read tuner lock bit */
	*lock = 1;
	return SUCCESS;
}

